/*
MIT License

Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#ifndef ScriptSemantics_abstract_h
#define ScriptSemantics_abstract_h

/*! \file ScriptSemantics_abstract.h
    \brief Класс разбора SBL операции, заданной в виде ноды семантического дерева (ast::Node)
*/

#include "ScriptOperationCode.h"
#include "ScriptOperationParser.h"
#include "ScriptSemanticsStructures.h"

#include "simodo/interpret/ModuleManagement_interface.h"
#include "simodo/interpret/Interpret_interface.h"

namespace simodo::interpret
{
    class ScriptSemantics_abstract: public SemanticModule_interface, 
                                    public Notification_interface
    {
        ModuleManagement_interface &    _module_management;

        Interpret_interface *           _interpret = nullptr;
        std::vector<OperationParser_t>  _operation_switchboard;

        /// \note При определении функции может потребоваться вложенный анализ объявлений
        /// \attention Перенесён в interpret::StackOfNames
        // std::vector<variable::Variable> _accumulated_contract_stack;
        /// \note Структура объекта может быть иерархической, поэтому - стек
        std::vector<ExecuteRecordStructure_data> _executeRecordStructure_stack;
        /// \note Объявление функции не может быть вложенным вне тела функции (в параметрах и типе возврата), 
        /// а тело анализируется отдельно. Поэтому стек не нужен
        FunctionDefinition_data         _function_definition_data;
        bool                            _function_definition_mode = false;
        std::vector<std::shared_ptr<variable::Object>> _functions_for_closures;
        std::vector<ExecuteCycle_data>  _cycle_data_stack;
        std::function<void(const FlowLayerInfo &)> _cycle_body_callback;
        std::function<void(const FlowLayerInfo &)> _cycle_condition_callback;
        std::function<void(const FlowLayerInfo &)> _for_source_callback;
        std::function<void(const FlowLayerInfo &)> _for_iterator_callback;
        std::function<void(const FlowLayerInfo &)> _for_body_callback;
        bool                            _setter_in_stack = false;
        FiberFlowMode                   _fiber_flow_mode = FiberFlowMode::None;

    protected:
        std::vector<variable::Variable> _functions_for_analyze;
        std::vector<variable::Variable> _functions_for_analyze_called;

    public:
        ScriptSemantics_abstract() = delete;
        ScriptSemantics_abstract(ModuleManagement_interface & module_management);

        virtual ~ScriptSemantics_abstract() override;

    public:
        const Interpret_interface &inter()         const { return *_interpret; }
        Interpret_interface &   inter()                  { return *_interpret; }

        const StackOfNames_interface & stack()     const { return _interpret->stack(); }
        StackOfNames_interface & stack()                 { return _interpret->stack(); }

        Notification_interface & notification()          { return *this; }

    // SemanticModule_interface
    public:
        virtual void            setInterpret(Interpret_interface * inter)       override { _interpret = inter; }
        virtual void            reset()                                         override;
        virtual InterpretState  before_start()                                  override;
        virtual InterpretState  before_finish(InterpretState state)             override;
        virtual bool            isOperationExists(ast::OperationCode operation_code) const  override;
        virtual InterpretState  performOperation(const ast::Node & op)          override;
        virtual bool            checkSemanticName(const std::u16string & semantic_name) const override
                                { return semantic_name == SCRIPT_HOST_NAME; }
        /// \info Это определение требуется только для того, чтобы успокоить clang-diagnostic
        virtual bool            checkInterpretType(InterpretType )        const override
                                { return false; }

    // Notification_interface
    protected:
        virtual void            notifyDeclared(const variable::Variable & ) override {}
        virtual void            notifyInitiated(const variable::Variable & ) override {}
        virtual void            notifyNameUsed(const variable::Variable & , const inout::TokenLocation & ) override {}      
        virtual void            notifyRef(const inout::Token & , const std::u16string & ) override {}
        virtual void            notifyBeforeFunctionCalling(const variable::Variable & ) override {}
        virtual void            notifyRemoteFunctionLaunch(const inout::TokenLocation & ) override {}
    
    // Группа методов непосредственно отвечающих за выполнение уже разобранной и подготовленной операции.
    // Различные типы интерпретаторов могут перехватывать (перегружать) эти методы для выполнения 
    // специальных действий.

    public:
        virtual InterpretState   executeNone             ();
        virtual InterpretState   executePushConstant     (const ast::Node & op);
        virtual InterpretState   executePushVariable     (const inout::Token & variable_name);
        virtual InterpretState   executeObjectElement    (const ast::Node & op);
        virtual InterpretState   executeFunctionCall     (const ast::Node & op);
        virtual InterpretState   executeProcedureCheck   ();
        virtual InterpretState   executePrint            (bool need_detailed_report);
        virtual InterpretState   executeBlock            (bool is_beginning_of_block, const ast::Node & op);
        virtual InterpretState   executePop              ();

        virtual InterpretState   executeReference        (const ast::Node & op);
        virtual InterpretState   executeArrayElement     (const ast::Node & op);

        virtual InterpretState   executeObjectStructure  (const ast::Node & op);
        virtual InterpretState   executeArrayStructure   (const ast::Node & op);
        virtual InterpretState   executeImport           (const inout::Token & path, const ast::Node & params);
        virtual InterpretState   executeContract         (const ast::Node & op);
        virtual InterpretState   executeAnnouncement     ();
        virtual InterpretState   executeDeclaration      (const ast::Node & op);
        virtual InterpretState   executeDeclarationCompletion();
        virtual InterpretState   executeGroupInitialize  (const ast::Node & op, std::vector<const inout::Token *> ids, const ast::Node & rvalue_ops);
        virtual InterpretState   executeAssignment       (const ast::Node & op);
        virtual InterpretState   executePostAssignment   (const ast::Node & op);

        virtual InterpretState   executeUnary            (const ast::Node & op);
        virtual InterpretState   executeLogical          (const ast::Node & op);
        virtual InterpretState   executeCompare          (const ast::Node & op);
        virtual InterpretState   executeArithmetic       (const ast::Node & op);
        virtual InterpretState   executeConditional      (ScriptOperationCode code, const ast::Node & op_true, const ast::Node * op_false);
        virtual InterpretState   executeFunctionDefinition( const ast::Node * pop_closure, 
                                                            const ast::Node * pop_params,
                                                            const ast::Node * pop_return_type,
                                                            const ast::Node & op_body);
        virtual InterpretState   executeFunctionDefinitionEnd(const ast::Node & op);
        virtual InterpretState   executeFunctionTethered (const ast::Node & op);
        virtual InterpretState   executeReturn           (const ast::Node & op);
        virtual InterpretState   executeFor              (const inout::Token & dot,
                                                          const ast::Node * pop_variable, 
                                                          const ast::Node * pop_source, 
                                                          const ast::Node * pop_body);
        virtual InterpretState   executeWhile            (const inout::Token & dot,
                                                          const ast::Node * pop_expression, 
                                                          const ast::Node * pop_body);
        virtual InterpretState   executeDoWhile          (const inout::Token & dot,
                                                          const ast::Node * pop_body, 
                                                          const ast::Node * pop_expression);
        virtual InterpretState  executeBreak            (const ast::Node & op);
        virtual InterpretState  executeContinue         (const ast::Node & op);

        virtual InterpretState  executeApply            (const ast::Node & op, const std::vector<inout::Token> & names);
        // virtual InterpretState  executeAutoDefine       (const ast::Node & op);

        virtual InterpretState  executeFiberMake        (const ast::Node & op);
        virtual InterpretState  executeFiberWait        (const ast::Node & op);
        virtual InterpretState  executeFiberPushPull   (const ast::Node & op);
        virtual InterpretState  executeFiberFlow        (const ast::Node & op);

        virtual InterpretState  executeCheckState       (const ast::Node & op);

    protected:
        void                    appendContractProperties(const variable::Variable & from, variable::Variable & to);
        variable::Variable      createVariable( const variable::Variable & contract, 
                                                const inout::Token & variable_token, 
                                                const std::u16string & origin);

        bool                    maybeContract(const variable::Variable & var) const;

        void                    unary(ScriptOperationCode opcode, const inout::TokenLocation & location);
        void                    logical(ScriptOperationCode opcode, const inout::TokenLocation & location);
        void                    compare(ScriptOperationCode opcode, const inout::TokenLocation & location);
        void                    arithmetic(ScriptOperationCode opcode, const inout::TokenLocation & location);
        variable::ValueType     getType4TypeConversion(ScriptOperationCode opcode, variable::ValueType type1, variable::ValueType type2) const;

        variable::Value         performLogicalOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const;
        variable::Value         performCompareEqual(const variable::Value & op1, const variable::Value & op2) const;
        variable::Value         performCompareLess(const variable::Value & op1, const variable::Value & op2) const;
        variable::Value         performCompareLessOrEqual(const variable::Value & op1, const variable::Value & op2) const;
        variable::Value         performArithmeticOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const;

        std::string             makeModulePath(const inout::Token & path, const std::string & parent_path);
        bool                    prepareForSource();
        void                    initiateCallbacks();
        bool                    hidden(const variable::Variable & var);
        bool                    tethered(const variable::Variable & var);
        void                    setParentObjectForFunction( variable::Variable & function, 
                                                            std::shared_ptr<variable::Object> parent_object);

        variable::Variable &    prepareFiberVariableOrigin();
    };
}

#endif // ScriptSemantics_abstract_h
